
#include <cmath>
#include <string>

#include <promote/StopWatch.hpp>
#include <promote/Timestamp.hpp>
#include <promote/TimeUtils.hpp>

#include "ExpiryCalculator.hpp"
#include "ExpiryCalculatorTest.hpp"

using namespace provident;
using namespace provident::test;

static std::string testCalendar();
static double perfTestCalendar(bool cached);

void provident::test::unitTestExpiryCalculator()
{
    printf("calendar year: %s\n", testCalendar().c_str());
}

void provident::test::perfTestExpiryCalculator()
{
    printf("calendar year <not cached>: %g ns\n", perfTestCalendar(false));
    printf("calendar year <cached>: %g ns\n", perfTestCalendar(true));
}

static std::string testCalendar()
{
    ExpiryCalculator calc(ExpiryCalculator::CALENDAR_YEAR);
    promote::Timestamp timestamp;
    double expected(0);
    for(int ii = 1; ii != 1000; ++ii) {
        timestamp.dayOfMonth(timestamp.dayOfMonth()+1);
        expected += 1.0 / (timestamp.isLeapYear() ? 366 : 365);
        double years(calc.yearsTilExpiry(timestamp.month(),
                                         timestamp.dayOfMonth(),
                                         timestamp.year()));
        if( fabs(years - expected) / expected > 0.1 ) {
            return "fail";
        }
    }
    return "pass";
}

static double perfTestCalendar(bool cached)
{
    std::size_t const iters(100);
    std::size_t const count(1000);
    promote::StopWatch stopWatch;
    promote::Timestamp ts[1000];

    for(int ii = 0; ii != count; ++ii) {
        ts[ii].dayOfMonth(ts[ii].dayOfMonth()+ii);
    }

    for(std::size_t iter = 0; iter != iters; ++iter) {
        ExpiryCalculator calc(ExpiryCalculator::CALENDAR_YEAR);
        if( cached ) {
            for(int ii = 1; ii != count; ++ii) {
                calc.yearsTilExpiry(ts[ii].month(),ts[ii].dayOfMonth(),ts[ii].year());
            }
        }

        stopWatch.start();
        for(std::size_t ii = 0; ii != count; ++ii) {
            calc.yearsTilExpiry(ts[ii].month(),ts[ii].dayOfMonth(),ts[ii].year());
        }
        stopWatch.stop();
    }

    return (promote::getTimevalAsMicros(stopWatch.elapsed()) * 1000.0) / (iters * count);
}

